<?php

namespace PhpYes\Com;

use PhpYes\Yes;

class ExampleSession
{
    private static $isExtractSession = false;
    private static $isExtractSessionData = false;

    /**
     * session id, 可以设置
     * @var string
     */
    public static $sessionId = 'sid';

    /**
     * session key 的前缀, 可以设置
     * @var null|string
     */
    public static $sessionKeyPrefix;

    /**
     * session 的生存时间, 默认 1 天, 可以设置
     * @var int
     */
    public static $ttl = 86400; // 1 day = 24 * 60 * 60 second

    /** @var string */
    private static $sKey;
    /** @var int */
    private static $exp;
    /** @var array|null */
    private static $sessionData;
    /** @var UserEntity|null */
    private static $userEntity;

    /**
     * @return null|\Redis
     * @throws HttpException
     */
    private static function getSessionRedis()
    {
        return RedisManager::getSingleObj()->getRedis('RedisDb');
    }

    /**
     * @return string
     */
    private static function genSessionKey()
    {
        $prefix = empty(self::$sessionKeyPrefix) ? null : trim(self::$sessionKeyPrefix);
        if (empty($prefix)) {
            return strval(time()) . '-' . Base64UrlSafeEncoder::encode(openssl_random_pseudo_bytes(12));
        } else {
            return $prefix . '-' . strval(time()) . '-' . Base64UrlSafeEncoder::encode(openssl_random_pseudo_bytes(8));
        }
    }

    public static function init()
    {
        if (self::$isExtractSession) {
            return;
        }
        self::$isExtractSession = true;

        // 跨域开发时需要配置session的cookie种到的域, 配置文件可以修改，只要能取到配置就行
        $config = Yes::$p->config_config();
        $sessionDomain = empty($config['sessionDomain']) ? null : trim($config['sessionDomain']);

        $time = time();
        $sessionRedis = self::getSessionRedis();

        $sid = empty($_COOKIE[self::$sessionId]) ? null : trim($_COOKIE[self::$sessionId]);
        if (empty($sid)) {
            $sKey = null;
            $exp = 0;
        } else {
            $pair = explode('.', $sid);
            $sKey = empty($pair[0]) ? null : trim($pair[0]);
            $exp = empty($pair[1]) ? 0 : intval($pair[1]);
        }

        if (empty($sKey)) {
            self::$sKey = self::genSessionKey();
            self::$exp = $time + self::$ttl;
            setcookie(self::$sessionId, self::$sKey . '.' . self::$exp, self::$exp, '/', $sessionDomain);
            // $sessionRedis->setex(self::$sKey, self::$ttl, json_encode(array('uid' => 0))); // 向 session 中放数据时才会向 session redis 中存放数据，例如用户登录绑定uid到session
        } else {
            self::$sKey = $sKey;
            if ($exp - $time < self::$ttl / 2) {
                self::$exp = $time + self::$ttl;
                setcookie(self::$sessionId, self::$sKey . '.' . self::$exp, self::$exp, '/', $sessionDomain);
                $sessionRedis->expire(self::$sKey, self::$ttl);
            } else {
                self::$exp = $exp;
            }
        }
    }

    /**
     * @return string
     */
    public static function getSKey()
    {
        return self::$sKey;
    }

    /**
     * @return int
     */
    public static function getExp()
    {
        return self::$exp;
    }

    /**
     * @return array|null
     */
    public static function getSessionData()
    {
        if (!self::$isExtractSessionData) {
            self::$isExtractSessionData = true;

            $sessionRedis = self::getSessionRedis();
            $sessionData = $sessionRedis->get(self::$sKey);
            if ($sessionData) {
                self::$sessionData = json_decode($sessionData, true);
            }
        }

        return self::$sessionData;
    }

    /**
     * @param string $key
     * @param mixed $val
     */
    public static function put($key, $val)
    {
        $key = empty($key) ? null : trim($key);
        if (empty($key)) {
            return;
        }

        $sessionData = self::getSessionData();
        if (empty($sessionData)) {
            $sessionData = array($key => $val);
        } else {
            $sessionData[$key] = $val;
        }

        $sessionRedis = self::getSessionRedis();
        $sKey = self::getSKey();
        if ($sessionRedis && $sKey) {
            $sessionRedis->setex($sKey, self::$ttl, json_encode($sessionData));
            self::$isExtractSessionData = false;
        }
    }

    /**
     * 将用户和 session 绑定起来
     * @param $uid
     */
    public static function bindUser($uid)
    {
        $uid = intval($uid);
        self::put('uid', $uid);
    }

    /**
     * 将用户和 session 解除绑定
     */
    public static function unbindUser()
    {
        self::put('uid', 0);
    }

    /**
     * @return int
     */
    public static function getUid()
    {
        $sessionData = self::getSessionData();

        return empty($sessionData['uid']) ? 0 : intval($sessionData['uid']);
    }

    /**
     * @return UserEntity|null
     */
    public static function getUserEntity()
    {
        if (empty(self::$userEntity)) {
            $uid = self::getUid();
            if ($uid) {
                self::$userEntity = UserEntity::getSingleObj()->findById($uid);
            }
        }

        return self::$userEntity;
    }

}
